package cgHttp

import (
	"bytes"
	"io"
	"log"
	"net/http"

	"gitee.com/zzzcommon/common-go/cgUtils"
	"github.com/gin-gonic/gin"
)

type _configFunc func(e *gin.RouterGroup)

var (
	Success                   = NewErrorWithStatusCode(http.StatusOK, http.StatusText(http.StatusOK))
	ServerError               = NewErrorWithStatusCode(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
	NotFound                  = NewErrorWithStatusCode(http.StatusNotFound, http.StatusText(http.StatusNotFound))
	configFuncs []_configFunc = []_configFunc{}
)

type Error struct {
	StatusCode int    `json:"-"`
	Code       int    `json:"code"`
	Msg        string `json:"message"`
}

func OtherError(message string) *Error {
	return NewErrorWithStatusCode(http.StatusInternalServerError, message)
}

func (e *Error) Error() string {
	return e.Msg
}

func NewError(statusCode int, code int, msg string) *Error {
	return &Error{
		StatusCode: statusCode,
		Code:       code,
		Msg:        msg,
	}
}

func NewErrorWithStatusCode(statusCode int, msg string) *Error {
	return &Error{
		StatusCode: statusCode,
		Code:       statusCode,
		Msg:        msg,
	}
}

// 502, 404处理
func HandleNotFound(c *gin.Context) {
	err := NotFound
	c.JSON(err.StatusCode, err)
}

// 跨域处理
func CorsMiddleware() gin.HandlerFunc {
	return func(context *gin.Context) {
		method := context.Request.Method
		context.Header("Access-Control-Allow-Origin", "*")
		context.Header("Access-Control-Allow-Headers", "Content-Type,AccessToken,X-CSRF-Token, Authorization, Token")
		context.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS")
		context.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type")
		context.Header("Access-Control-Allow-Credentials", "true")
		if method == "OPTIONS" {
			context.AbortWithStatus(http.StatusNoContent)
		}
		context.Next()
	}
}

// 添加路由包
func AddRouter(f _configFunc) {
	configFuncs = append(configFuncs, f)
}

// 安装已经添加的路由包
func InstallRouters(e *gin.RouterGroup) {
	for _, f := range configFuncs {
		f(e)
	}
	configFuncs = configFuncs[0:0]
	configFuncs = nil
}

// 错误处理中间件
func ErrHandlerMiddleware(retError func(c *gin.Context, err *Error)) gin.HandlerFunc {
	return func(c *gin.Context) {
		defer func() {
			if err := recover(); err != nil {
				var Err *Error
				if e, ok := err.(*Error); ok {
					Err = e
				} else if e, ok := err.(error); ok {
					Err = OtherError(e.Error())
				} else {
					Err = ServerError
				}
				// 回调自己处理错误
				retError(c, Err)
			}
		}()
		c.Next()
	}
}

func _getLogger(l *log.Logger) *log.Logger {
	if l != nil {
		return l
	}
	return log.Default()
}

// 自定义一个结构体，实现 gin.ResponseWriter interface
type responseWriter struct {
	gin.ResponseWriter
	b *bytes.Buffer
}

// 重写 Write([]byte) (int, error) 方法
func (w responseWriter) Write(b []byte) (int, error) {
	//向一个bytes.buffer中写一份数据来为获取body使用
	w.b.Write(b)
	//完成gin.Context.Writer.Write()原有功能
	return w.ResponseWriter.Write(b)
}

type LogHandlerParams struct {
	MaxBodyLogLength int
	Logger           *log.Logger
}

func LogHandler(params *LogHandlerParams, logResponse func(c *gin.Context) bool) gin.HandlerFunc {
	if params == nil {
		params = &LogHandlerParams{
			MaxBodyLogLength: 1024,
		}
	}
	_logger := _getLogger(params.Logger)
	return func(c *gin.Context) {
		if logResponse != nil && !logResponse(c) {
			c.Next()
			return
		}

		// request log
		_rqBodyBs, err := io.ReadAll(c.Request.Body)
		if err != nil {
			_logger.Println(err)
		} else {
			_logger.Printf("request info:\nurl: %s\nip: %s\nmethod: %s\nbody: %s", c.Request.RequestURI, c.Request.RemoteAddr, c.Request.Method, _rqBodyBs)
		}
		c.Request.Body = io.NopCloser(bytes.NewBuffer(_rqBodyBs))

		writer := responseWriter{
			c.Writer,
			bytes.NewBuffer([]byte{}),
		}
		c.Writer = writer

		c.Next()

		responseBodyString := writer.b.String()

		if cgUtils.Len(responseBodyString) > params.MaxBodyLogLength {
			_logger.Printf("resonse info:\nbody: %s...(length more than %d, cut off.)", cgUtils.Substring(responseBodyString, 0, params.MaxBodyLogLength), params.MaxBodyLogLength)
		} else {
			_logger.Printf("response info:\nbody: %s", responseBodyString)
		}

		writer.b = nil
	}
}
